package com.nhim.cms.admin.client.eldf.ui;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.activity.shared.Activity;
import com.google.gwt.cell.client.AbstractEditableCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.ClickableTextCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.ProvidesKey;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.nhim.cms.admin.client.SpacerColumn;
import com.nhim.cms.admin.client.place.ElementDefinitionPlace;
import com.nhim.cms.admin.client.style.Styles;
import com.nhim.cms.admin.shared.ElementDefinitionProxy;
import com.nhim.cms.admin.shared.ElementDefinitionRequest;
import com.nhim.cms.admin.shared.ElementTemplateProxy;
import com.nhim.cms.admin.shared.ElementTemplateRequest;
import com.nhim.cms.admin.shared.MessageManager;
import com.nhim.cms.admin.shared.NhimCMSAdminRequestFactory;

public class ElementDefinition extends Composite implements Activity {
	Logger logger = Logger
			.getLogger(ElementDefinitionTemplate.class.toString());
	private static ElementDefinitionUiBinder uiBinder = GWT
			.create(ElementDefinitionUiBinder.class);

	interface ElementDefinitionUiBinder extends
			UiBinder<Widget, ElementDefinition> {
	}
	private Listener listener;
	/**
	 * Custom listener for this widget.
	 */
	public interface Listener {

		/**
		 * Called when the user delete ElementDefinition.
		 * 
		 */
		void onDeleted();
	}
	public void setListener(Listener listener) {
		this.listener = listener;
	}

	final NhimCMSAdminRequestFactory requestFactory;
	MessageManager message = new MessageManager();
	ElementDefinitionTemplate elementTemplate = new ElementDefinitionTemplate();
	ElementDefinitionProxy elementDefinitionProxy;
	ElementTemplateProxy elementTemplateProxy;
	Boolean isNew = true;

	public ElementDefinition(final NhimCMSAdminRequestFactory requestFactory) {
		this.requestFactory = requestFactory;
		createTable();
		initWidget(uiBinder.createAndBindUi(this));
		elementTemplate.getAddButton().addClickHandler(new ClickHandler() {

			public void onClick(ClickEvent event) {
				List<String> validation = elementTemplate.validate();
				if (validation.size() > 0) {
					message.showErrorPopup(validation);
					return;
				}

				if (elementTemplate.isNew()) {
					requestFactory
							.elementTemplateRequest()
							.isNameExist(elementDefinitionProxy.getId(),
									elementTemplate.getName())
							.fire(new Receiver<Boolean>() {

								@Override
								public void onSuccess(Boolean response) {
									if (response)
										message.showErrorPopup("Element Template with the given name already exist.");
									else {
										saveElementTemplate(true);

									}
								}
							});
				} else {
					saveElementTemplate(false);

				}
			}
		});

	}

	private void saveElementTemplate(Boolean isNew) {
		try {
			ElementTemplateRequest thisRequest = requestFactory
					.elementTemplateRequest();
			final Integer currentElementId = this.elementDefinitionProxy.getId();
			if (isNew) {
				this.elementTemplateProxy = thisRequest
						.create(ElementTemplateProxy.class);
				this.elementTemplateProxy.setElementDefinition(this.elementDefinitionProxy);
			
			} else {
				this.elementTemplateProxy = thisRequest
						.edit(this.elementTemplateProxy);
				
			}
			elementTemplate.save(this.elementTemplateProxy);
			thisRequest.persit(this.elementTemplateProxy).fire(new Receiver<ElementTemplateProxy>(){

				@Override
				public void onSuccess(ElementTemplateProxy response) {
					// populate template table
					requestFactory
							.elementTemplateRequest()
							.findByElementDefinitionId(currentElementId)
							.fire(new Receiver<List<ElementTemplateProxy>>() {

								@Override
								public void onSuccess(
										List<ElementTemplateProxy> response) {
									setElementTemplates(response);
								}
							});
					elementTemplate.hide();
					
				}});
			
		} catch (Exception ex) {
			Window.alert(ex.getMessage());
		}

	}

	private ElementDefinitionPlace place;
	@UiField
	Anchor addTemplate;
	@UiField
	ElementType elementType;
	@UiField
	Button btnCancel;
	@UiField
	Button btnSave;
	@UiField
	Button btnDelete;
	@UiField
	TextBox txtName;
	@UiField
	TextBox txtDescription;
	@UiField
	CheckBox chkSearchable;
	@UiField
	CheckBox chkMetadata;
	@UiField
	HTMLPanel templatePanel;

	@UiField(provided = true)
	CellTable<ElementTemplateProxy> cellTable;
	private List<AbstractEditableCell<?, ?>> editableCells;

	public static final ProvidesKey<ElementTemplateProxy> KEY_PROVIDER = new ProvidesKey<ElementTemplateProxy>() {
		public Object getKey(ElementTemplateProxy item) {
			return item == null ? null : item.getId();
		}
	};
	private ListDataProvider<ElementTemplateProxy> dataProvider = new ListDataProvider<ElementTemplateProxy>();

	public HasClickHandlers getCancelLink() {
		return btnCancel;
	}

	public String mayStop() {
		return null;
	}

	public void onCancel() {

	}

	public void onStop() {

	}

	private void setDisplay(ElementDefinitionProxy response) {
		this.elementDefinitionProxy = response;
		this.txtName.setText(response.getName());
		this.txtDescription.setText(response.getDescription());
		this.chkSearchable.setValue(response.getIsSearchable());
		this.chkMetadata.setValue(response.getIsMetadata());
		requestFactory.elementTemplateRequest()
				.findByElementDefinitionId(response.getId())
				.fire(new Receiver<List<ElementTemplateProxy>>() {

					@Override
					public void onSuccess(List<ElementTemplateProxy> response) {
						setElementTemplates(response);
					}
				});
		elementType.setData(elementDefinitionProxy);

	}

	private void clearDisplay() {
		this.txtName.setText("");
		this.txtDescription.setText("");
		this.chkSearchable.setValue(false);
		this.chkMetadata.setValue(false);
		elementType.setData(null);
		List<ElementTemplateProxy> provider = dataProvider.getList();
		provider.clear();
		cellTable.redraw();

	}

	public void start(AcceptsOneWidget panel, EventBus eventBus) {
		clearDisplay();

		if (place.getElementDefinitionId() != null) {

			requestFactory.find(place.getElementDefinitionId()).fire(
					new Receiver<ElementDefinitionProxy>() {

						@Override
						public void onSuccess(ElementDefinitionProxy response) {

							setDisplay(response);
						}
					});
			this.isNew = false;
			this.templatePanel.setVisible(true);
			this.btnDelete.setVisible(true);

		} else {
			this.isNew = true;
			this.templatePanel.setVisible(false);
			this.btnDelete.setVisible(false);
		}
		panel.setWidget(this);

	}

	private void setElementTemplates(List<ElementTemplateProxy> templates) {
		List<ElementTemplateProxy> provider = dataProvider.getList();
		provider.clear();
		this.elementTemplateProxy = null;
		if (templates != null && templates.size() > 0) {
			for (ElementTemplateProxy template : templates) {
				{
					try {

						provider.remove(template);
						provider.add(template);
					} catch (Exception ex) {

						logger.log(Level.SEVERE, ex.getMessage());
					}
				}
			}
		}
		cellTable.redraw();
	}

	public void updateForPlace(ElementDefinitionPlace place) {
		this.place = place;

	}

	/**
	 * The resources applied to the table.
	 */
	interface TableResources extends CellTable.Resources {
		@Source({ CellTable.Style.DEFAULT_CSS,
				"../../style/normalCellTable.css" })
		TableStyle cellTableStyle();
	}

	/**
	 * The styles applied to the table.
	 */
	interface TableStyle extends CellTable.Style {

	}

	public void createTable() {
		CellTable.Resources resources = GWT.create(TableResources.class);
		editableCells = new ArrayList<AbstractEditableCell<?, ?>>();
		cellTable = new CellTable<ElementTemplateProxy>(100, resources,
				KEY_PROVIDER);
		Styles.Common common = Styles.common();
		cellTable.addColumnStyleName(0,
				common.elementDefinitionTemplateEditColumn());
		cellTable.addColumnStyleName(1, common.elementTemplateSpacerColumn());
		cellTable.addColumnStyleName(2,
				common.elementDefinitionTemplateRemoveColumn());

		addColumn(new ClickableTextCell(), new GetValue<String>() {
			public String getValue(ElementTemplateProxy template) {
				return template.getName();
			}
		}, new FieldUpdater<ElementTemplateProxy, String>() {
			public void update(int index, ElementTemplateProxy object,
					String value) {
				elementTemplate.showTemplatePopup(object, false);
				elementTemplate.setGlassEnabled(true);
				elementTemplate.center();
				elementTemplate.show();
				elementTemplateProxy = object;

			}
		});

		cellTable.addColumn(new SpacerColumn<ElementTemplateProxy>());

		addColumn(new ClickableTextCell(), new GetValue<String>() {
			public String getValue(ElementTemplateProxy template) {
				return "Remove";
			}
		}, new FieldUpdater<ElementTemplateProxy, String>() {
			public void update(int index, final ElementTemplateProxy object,
					String value) {
				if (!object.getName().toLowerCase().equals("default")) {
					if (Window
							.confirm("Are you sure you want to delete this template?")) {
						final List<ElementTemplateProxy> provider = dataProvider
								.getList();
						if (object.getId() != null) {
							requestFactory.elementTemplateRequest()
									.remove(object.getId())
									.fire(new Receiver<Boolean>() {

										@Override
										public void onSuccess(Boolean response) {
											if (!response)
												message.showErrorPopup("There was an error when deleting element template.");
											else
												provider.remove(object);

										}
									});
						} else {
							provider.remove(object);
						}
						cellTable.redraw();
					}
				}
			}
		});

		dataProvider.addDataDisplay(cellTable);
	}

	/**
	 * Get a cell value from a record.
	 * 
	 * @param <C>
	 *            the cell type
	 */
	private static interface GetValue<C> {
		C getValue(ElementTemplateProxy tempate);
	}

	/**
	 * Add a column with a header.
	 * 
	 * @param <C>
	 *            the cell type
	 * @param cell
	 *            the cell used to render the column
	 * @param headerText
	 *            the header string
	 * @param getter
	 *            the value getter for the cell
	 */
	private <C> void addColumn(Cell<C> cell, final GetValue<C> getter,
			FieldUpdater<ElementTemplateProxy, C> fieldUpdater) {
		Column<ElementTemplateProxy, C> column = new Column<ElementTemplateProxy, C>(
				cell) {
			public C getValue(ElementTemplateProxy object) {
				return getter.getValue(object);
			}
		};
		column.setFieldUpdater(fieldUpdater);
		if (cell instanceof AbstractEditableCell<?, ?>) {
			editableCells.add((AbstractEditableCell<?, ?>) cell);
		}
		cellTable.addColumn(column);

	}

	@UiHandler("btnDelete")
	void handleButtonDeleteClick(ClickEvent e) {

		if (Window
				.confirm("Are you sure you want to delete this Element Definition?")) {
			requestFactory.elementDefinitionRequest().remove(this.elementDefinitionProxy.getId()).fire(new Receiver<Boolean>(){

				@Override
				public void onSuccess(Boolean response) {
					if(response)
						listener.onDeleted();
					else
						message.showErrorPopup("An error has occurred while deleting Element Definition.");
					
				}});
		}
	}

	private void save() {
		try {

			ElementDefinitionRequest thisRequest = requestFactory
					.elementDefinitionRequest();
			if (isNew) {
				ElementDefinitionProxy newElement = thisRequest
						.create(ElementDefinitionProxy.class);
				this.elementDefinitionProxy = newElement;
			} else {
				this.elementDefinitionProxy = thisRequest
						.edit(this.elementDefinitionProxy);
			}
			this.elementDefinitionProxy.setName(this.txtName.getText());

			this.elementDefinitionProxy.setDescription(this.txtDescription
					.getText());
			this.elementDefinitionProxy.setIsMetadata(this.chkMetadata
					.getValue());

			this.elementDefinitionProxy.setIsSearchable(this.chkSearchable
					.getValue());

			elementType.save(this.elementDefinitionProxy);
			List<ElementTemplateProxy> provider = dataProvider.getList();

			this.elementDefinitionProxy
					.setElementTemplates(new HashSet<ElementTemplateProxy>(
							provider));

			thisRequest.persit(this.elementDefinitionProxy).fire(
					new Receiver<ElementDefinitionProxy>() {

						@Override
						public void onSuccess(ElementDefinitionProxy response) {
							isNew = false;
							templatePanel.setVisible(true);
							btnDelete.setVisible(true);
							setDisplay(response);
						}
					});

		} catch (Exception ex) {
			Window.alert(ex.getMessage());
		}

	}

	private List<String> validate() {
		List<String> validation = new ArrayList<String>();
		if (this.txtName.getText().isEmpty())
			validation.add("The Element Definition name can not be empty.");
		validation.addAll(elementType.validate());
		return validation;
	}

	@UiHandler("btnSave")
	void handleButtonSaveClick(ClickEvent e) {
		List<String> validation = validate();
		if (validation.size() > 0) {
			message.showErrorPopup(validation);
		} else {
			if (!this.txtName.getText().isEmpty()
					&& isNew
					|| elementDefinitionProxy != null
					&& !this.txtName
							.getText()
							.toLowerCase()
							.equals(this.elementDefinitionProxy.getName()
									.toLowerCase())) {
				requestFactory.elementDefinitionRequest()
						.isNameExist(this.txtName.getText())
						.fire(new Receiver<Boolean>() {

							@Override
							public void onSuccess(Boolean result) {
								if (result)
									message.showErrorPopup("Element Definition with the given name already exist.");
								else
									save();

							}
						});
			} else
				save();
		}

	}

	@UiHandler("addTemplate")
	void handleButtonAddTemplateClick(ClickEvent e) {
		elementTemplate.showTemplatePopup(elementTemplateProxy, true);
		elementTemplate.setGlassEnabled(true);
		elementTemplate.center();
		elementTemplate.show();

	}

}
